//package bom.example.common.config;
//
//import com.alibaba.druid.pool.DruidDataSource;
//import com.alibaba.fastjson.JSON;
//import org.apache.ibatis.cache.CacheKey;
//import org.apache.ibatis.executor.Executor;
//import org.apache.ibatis.executor.statement.StatementHandler;
//import org.apache.ibatis.mapping.BoundSql;
//import org.apache.ibatis.mapping.MappedStatement;
//import org.apache.ibatis.mapping.ParameterMapping;
//import org.apache.ibatis.plugin.*;
//
//import org.apache.ibatis.session.Configuration;
//import org.apache.ibatis.session.ResultHandler;
//import org.apache.ibatis.session.RowBounds;
//
//import java.lang.reflect.Method;
//import java.util.ArrayList;
//import java.util.List;
//import java.util.Properties;
//
//import com.alibaba.druid.sql.SQLUtils;
//import org.springframework.stereotype.Component;
//
//
///**
// * @author X.I.O
// * @title: MybatisInterceptor
// * @projectName springboot
// * @description: TODO
// * @date 2021/12/2 0:23
// */
//@Component
//@Intercepts({@org.apache.ibatis.plugin.Signature(
//        type = Executor.class,
//        method = "update",
//        args = {MappedStatement.class, Object.class}),
//        @Signature(type = Executor.class,
//                method = "query",
//                args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class,
//                        CacheKey.class, BoundSql.class})})
//public class MybatisInterceptor implements Interceptor {
//
//    /**
//     * intercept方法就是要进行拦截的时候要执行的方法。
//     */
//    @Override
//    public Object intercept(Invocation invocation) throws Throwable {
//        Object[] args = invocation.getArgs();
//
//        MappedStatement ms = (MappedStatement) args[0];
//        ms.getStatementType();
////        当前SQL使用的是哪个Mapper,即哪个Mapper类
//        String mapper = ms.getResource();
//        Configuration configuration = ms.getConfiguration();
////        执行当前SQL的Mapper id,其组成 [ 类型.方法 ]
//        String mapperID = ms.getId();
//
////        获取当前执行的SQL使用哪个数据源,我这里的数据源组件使用的是Druid，如果使用c3p0或者其他,则需要查看相关API,一般来降一个项目可能会配多个数据源,但是数据源组件都会使用一个
//        DruidDataSource dataSource = (DruidDataSource) configuration.getEnvironment().getDataSource();
////        获取数据库的类型[即mysql,或者oracle等等]
//        String dbType = dataSource.getDataSourceStat().getDbType();
//
////        存放的是SQL的参数[它是一个实例对象]
//        Object parameterObject = args[1];
//        Object target = invocation.getTarget();
//        StatementHandler handler = configuration.newStatementHandler((Executor) target, ms, parameterObject, RowBounds.DEFAULT, null, null);
//
//        /**
//         * commandName.startsWith(增/删/改/查)，可以得到crud的具体类型[得到的是大写的INSERT UPDATE]
//         * method.getName()得到的name可能为update, query, flushStatements, commit, rollback, getTransaction, close, isClosed
//         */
//        String commandName = ms.getSqlCommandType().name();
//        Method method = invocation.getMethod();
//        String methodName = method.getName();
//
//        BoundSql boundSql = ms.getBoundSql(parameterObject);
////        这个ParameterMapping表示当前SQL绑定的是哪些参数,及参数类型,但并不是参数本身
//        List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
////        将参数值转成json字符串
//        String parameterObjects = JSON.toJSONString(boundSql.getParameterObject());
//
////        要拦截的SQL,通过拦截器的SQL 其不带参数
//        String srcSQL = boundSql.getSql();
////        返回拼装好参数的SQL
//        String retSQL = formatSQL(srcSQL, dbType, parameterObjects);
////        先执行当前的SQL方法,即通过当前拦截器的CRUD操作,因为我们要返回这个结果
//        Object result = invocation.proceed();
//
////        组装自己的SQL记录类
//        System.out.println("==============>");
//        System.out.println(srcSQL);
//        System.out.println(retSQL);
//
////        记录SQL
////        log.setStatement();
//        //记录影响行数
////        log.setResult(Integer.valueOf(Integer.parseInt(result.toString())));
////        记录时间
////        log.setOperateDate(new Date());
//        //TODO 还可以记录参数，或者单表id操作时，记录数据操作前的状态
//        //获取insertSqlLog方法
////        ms = ms.getConfiguration().getMappedStatement("insertSqlLog");
//        //替换当前的参数为新的ms
////        args[0] = ms;
//        //insertSqlLog 方法的参数为 log
//
//        //执行insertSqlLog方法
////        invocation.proceed();
//
////        返回拦截器拦截的执行结果
//        return result;
//    }
//
//    /**
//     * plugin方法是拦截器用于封装目标对象的，通过该方法我们可以返回目标对象本身，也可以返回一个它的代理。
//     * 当返回的是代理的时候我们可以对其中的方法进行拦截来调用intercept方法，当然也可以调用其他方法
//     * 对于plugin方法而言，其实Mybatis已经为我们提供了一个实现。Mybatis中有一个叫做Plugin的类，
//     * 里面有一个静态方法wrap(Object target,Interceptor interceptor)，通过该方法可以决定要返回的对象是目标对象还是对应的代理。
//     */
//    @Override
//    public Object plugin(Object o) {
////        只拦截Executor对象,减少目标被代理的次数
//        if (o instanceof Executor) {
//            return Plugin.wrap(o, this);
//        }
//        return o;
//    }
//
//    /**
//     * setProperties方法是用于在Mybatis配置文件中指定一些属性的
//     * 这个方法在Configuration初始化当前的Interceptor时就会执行
//     */
//    @Override
//    public void setProperties(Properties properties) {
//
//    }
//
//    /**
//     * @describe: 组装SQL
//     * @params:
//     * @Author: Kanyun
//     * @Date: 2018/8/22 10:53
//     */
//    public String formatSQL(String src, String dbType, String params) {
////        要传入的SQLUtils的参数集合,实际上虽然泛型是Object,但其实都是基本数据类型
//        List<Object> paramList = new ArrayList();
////        有了JSON字符串我们就可以通过正则表达式得到参数了
//        System.out.println(params);
////        需要注意的是这个SQLUtils是Druid数据源中的一个工具类,因为有现成的拼sql的工具,所以我就不再重复造轮子了,如果你的项目并没有使用Druid,
////        则需要将这个工具类加入到你的项目中
//        String retSQL = SQLUtils.format(src, dbType, paramList);
//        return retSQL;
//    }
//}
